package Ms

import (
	"fmt"
	"db2s/ETL"
	"db2s/global"
	"db2s/parDef"
	"db2s/ref"
	"strings"
)

func (pm PartitionMe) Set(parameter string, value any) (err error) {
	return ref.UpdateStructJsonValue(pm, parameter, value)
}
func (pm PartitionMe) Result(object string) global.Return {
	switch object {
	default:
		return global.Return{}
	}
}

func (pm PartitionMe) PartType(parameter parDef.Parameter) (result global.Return, err error) {
	var event = "[PartType]"
	var execSql []string
	execSql = append(execSql, fmt.Sprintf("SELECT DISTINCT \n\t\t"+
		"pf.type_desc AS PartitionMeth\n "+
		"FROM \n    "+
		"sys.partitions AS p\n "+
		"JOIN \n    "+
		"sys.tables AS t ON p.object_id = t.object_id\n "+
		"JOIN \n    "+
		"sys.indexes AS i ON p.object_id = i.object_id AND p.index_id = i.index_id\n "+
		"JOIN \n    "+
		"sys.partition_schemes AS ps ON i.data_space_id = ps.data_space_id\n "+
		"JOIN \n    "+
		"sys.partition_functions AS pf ON ps.function_id = pf.function_id\n "+
		"WHERE \n    "+
		"SCHEMA_NAME(t.schema_id) = '%v' \n "+
		"AND t.name = '%v'\n "+
		"AND p.partition_number > 0;",
		parameter.Object.Schema, parameter.Object.Table))
	if result, err = forExecQuerySql(event, parameter, ETL.String, execSql); err != nil {
		err = ref.ErrAddPrintf("PartType", err)
		return
	}
	return
}
func (pm PartitionMe) PartSum(parameter parDef.Parameter) (result global.Return, err error) {
	var event = "[PartSum]"
	var execSql []string
	execSql = append(execSql, fmt.Sprintf("SELECT COUNT(*) FROM (\n "+
		"SELECT \n\t\t"+
		"p.partition_number\n "+
		"FROM \n    "+
		"sys.partitions AS p\n "+
		"JOIN \n    "+
		"sys.tables AS t ON p.object_id = t.object_id\n "+
		"JOIN \n    "+
		"sys.indexes AS i ON p.object_id = i.object_id AND p.index_id = i.index_id\n "+
		"JOIN \n    "+
		"sys.data_spaces AS ds ON i.data_space_id = ds.data_space_id\n "+
		"JOIN \n    "+
		"sys.partition_schemes AS ps ON ds.data_space_id = ps.data_space_id\n "+
		"JOIN \n    "+
		"sys.partition_functions AS pf ON ps.function_id = pf.function_id\n "+
		"WHERE \n    "+
		"SCHEMA_NAME(t.schema_id) = '%v' \n"+
		"AND t.name = '%v'  \n    "+
		") AS a", parameter.Object.Schema, parameter.Object.Table))
	if result, err = forExecQuerySql(event, parameter, ETL.String, execSql); err != nil {
		err = ref.ErrAddPrintf("PartSum", err)
		return
	}
	if result.Result, err = stringAnyToInt64(result.Result); err != nil {
		err = ref.ErrAddPrintf("PartSum", err)
		return
	}
	return
}
func (pm PartitionMe) getPartMeth(parameter parDef.Parameter, partPlan string) (result global.Return, err error) {
	var event = "[getPartMeth]"
	var meth = parDef.PartMeth{
		First: "NONE",
		Sub:   "NONE",
	}
	var execSql = []string{fmt.Sprintf("SELECT \n"+
		"CASE pf.boundary_value_on_right\n"+
		"  WHEN 1 THEN 'RIGHT'\n"+
		"  ELSE 'LEFT' \n"+
		"END  [direction],\n"+
		"pf.type_desc [first]\n"+
		"FROM \n"+
		"sys.partition_schemes ps \n"+
		"JOIN \n"+
		"sys.partition_functions pf ON ps.function_id = pf.function_id \n"+
		"where \n"+
		"ps.name = '%v'", partPlan)}
	if result, err = forExecQuerySql(event, parameter, ETL.Map, execSql); err != nil {
		err = ref.ErrAddPrintf("getPartMeth", err)
		result.Result = meth
		return
	}
	if result.Result == nil {
		return
	}
	vv := mapNullActive(result.Result.(map[string]any))
	if err = ref.MapToStruct(vv, &meth); err != nil {
		err = ref.ErrAddPrintf("getPartMeth", err)
		return
	}
	result.Result = meth
	return
}
func (pm PartitionMe) getPartRule(parameter parDef.Parameter, partPlan string, partColString []string) (result global.Return, err error) {
	var event = "[getPartRule]"
	var execSql = []string{fmt.Sprintf("select \n\t"+
		"pv.function_id [functionId],\n\t"+
		"pv.boundary_id [seq],\n\t"+
		"pv.parameter_id [parameter],\n\t"+
		"pv.value [rule]\n"+
		"from \n"+
		"sys.partition_range_values pv\n"+
		"join \n"+
		"sys.partition_functions pf on pf.function_id = pv.function_id\n"+
		"join \n"+
		"sys.partition_schemes ps ON ps.function_id = pf.function_id\n"+
		"where \n\t"+
		"ps.name = '%v'\n"+
		"order by \n"+
		"pv.boundary_id,pv.parameter_id", partPlan)}
	if result, err = forExecQuerySql(event, parameter, ETL.SMap, execSql); err != nil {
		err = ref.ErrAddPrintf("getPartRule", err)
		return
	}
	if result.Result == nil {
		return
	}
	var pp = make(map[string][]map[string]any)
	for _, v := range result.Result.([]map[string]any) {
		vv := mapNullActive(v)
		key := fmt.Sprintf("%v@gt@%v", vv["functionId"], vv["seq"])
		if gv, ok := pp[key]; !ok {
			pp[key] = []map[string]any{vv}
		} else {
			pp[key] = append(gv, vv)
		}
	}
	var partRule []parDef.PartRule
	for key, v := range pp {
		var rule []string
		for k, vv := range v {
			rul := fmt.Sprintf("%v", vv["rule"])
			if n := strings.Index(rul, "+00"); n != -1 {
				rul = strings.TrimSpace(rul[:n])
			}
			switch strings.ToUpper(partColString[k]) {
			case "DATE":
				rul = strings.TrimSpace(strings.Split(rul, " ")[0])
			case "TIME":
				rul = strings.TrimSpace(strings.Split(rul, " ")[1])
			}
			if !strings.HasPrefix(rul, "'") {
				rul = "'" + rul + "'"
			}
			rule = append(rule, rul)
		}
		g := strings.Split(key, "@gt@")
		partRule = append(partRule, parDef.PartRule{
			Name:  fmt.Sprintf("part%v_%v", g[0], g[1]),
			Seq:   g[1],
			Rules: strings.Join(rule, ","),
		})
	}
	result.Result = partRule
	return
}
func (pm PartitionMe) getFirstPartColumn(parameter parDef.Parameter, partPlan string) (result global.Return, err error) {
	var event = "[getFirstPartColumn]"
	var execSql = []string{fmt.Sprintf("SELECT \n\t\t"+
		"c.name\n"+
		"FROM \n    "+
		"sys.indexes i\n"+
		"JOIN \n    "+
		"sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id\n"+
		"JOIN \n    "+
		"sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id\n"+
		"JOIN \n    "+
		"sys.tables t ON i.object_id = t.object_id\n"+
		"JOIN \n    "+
		"sys.data_spaces ds ON i.data_space_id = ds.data_space_id\n"+
		"JOIN \n    "+
		"sys.partition_schemes ps ON ds.data_space_id = ps.data_space_id\n"+
		"JOIN \n    "+
		"sys.partition_functions pf ON ps.function_id = pf.function_id\n"+
		"JOIN \n\t\t"+
		"sys.partition_parameters pp on pf.function_id = pp.function_id and pp.user_type_id = c.user_type_id\n"+
		"WHERE \n    "+
		"ps.name = '%v' \n    "+ // -- 替换为您的分区函数名称
		"AND i.type = 1 \n"+ // -- 聚集索引
		"ORDER BY \n"+
		"c.column_id", partPlan)}
	if result, err = forExecQuerySql(event, parameter, ETL.StringGarth, execSql); err != nil {
		err = ref.ErrAddPrintf("getFirstPartColumn", err)
		return
	}
	return
}
func (pm PartitionMe) getPartPlan(parameter parDef.Parameter) (result global.Return, err error) {
	var event = "[getPartPlan]"
	var execSql = []string{fmt.Sprintf("SELECT \n"+
		"p.name AS PartitionScheme \n"+
		"FROM \n"+
		"sys.tables t \n"+
		"JOIN \n"+
		"sys.indexes i ON t.object_id = i.object_id \n"+
		"JOIN \n"+
		"sys.partition_schemes p ON i.data_space_id = p.data_space_id \n"+
		"WHERE \n"+
		"i.type = 1 \n"+ //-- 1 indicates a clustered index, which is often used for partitioning
		"AND SCHEMA_NAME(t.schema_id) = '%v' \n"+ //-- 指定模式为 dbo
		"and t.name = '%v'", parameter.Object.Schema, parameter.Object.Table)}
	if result, err = forExecQuerySql(event, parameter, ETL.String, execSql); err != nil {
		err = ref.ErrAddPrintf("getPartPlan", err)
		return
	}
	return
}
func (pm PartitionMe) getPartColType(parameter parDef.Parameter, col string) (result global.Return, err error) {
	var event = "[getPartPlan]"
	var execSql = []string{fmt.Sprintf("select \n"+
		"data_type [Type] \n"+
		"from INFORMATION_SCHEMA.COLUMNS \n"+
		"WHERE table_schema='%v' \n"+
		"and table_name ='%v' \n"+
		"and COLUMN_NAME = '%v'", parameter.Object.Schema, parameter.Object.Table, col)}
	if result, err = forExecQuerySql(event, parameter, ETL.String, execSql); err != nil {
		err = ref.ErrAddPrintf("getPartColType", err)
		return
	}
	return
}
func (pm PartitionMe) newPartMetaBaseInit(parameter parDef.Parameter) (result parDef.PartMetaData, err error) {
	var res global.Return
	var partPlan string
	if res, err = pm.getPartPlan(parameter); err != nil {
		err = ref.ErrAddPrintf("newPartMetaBaseInit", err)
		return
	}
	if res.Result == nil {
		return
	}
	partPlan = fmt.Sprintf("%v", res.Result)
	if res, err = pm.getPartMeth(parameter, partPlan); err != nil {
		err = ref.ErrAddPrintf("newPartMetaBaseInit", err)
		return
	} else {
		switch res.Result.(type) {
		case parDef.PartMeth:
			result.Meth = res.Result.(parDef.PartMeth)
		}
	}
	if res, err = pm.getFirstPartColumn(parameter, partPlan); err != nil {
		err = ref.ErrAddPrintf("newPartMetaBaseInit", err)
		return
	} else {
		switch res.Result.(type) {
		case []string:
			result.PartCol = res.Result.([]string)
		}
	}
	var partColType []string
	for _, v := range result.PartCol {
		if res, err = pm.getPartColType(parameter, v); err != nil {
			err = ref.ErrAddPrintf("newPartMetaBaseInit", err)
			return
		} else {
			partColType = append(partColType, fmt.Sprintf("%v", res.Result))
		}
	}
	if res, err = pm.getPartRule(parameter, partPlan, partColType); err != nil {
		err = ref.ErrAddPrintf("newPartMetaBaseInit", err)
		return
	} else {
		switch res.Result.(type) {
		case []parDef.PartRule:
			result.RuleDesc = res.Result.([]parDef.PartRule)
		}
	}
	return
}
func (pm PartitionMe) PartMeta(parameter parDef.Parameter) (result global.Return, err error) {
	var baseResult parDef.PartMetaData
	var partName []string
	if baseResult, err = pm.newPartMetaBaseInit(parameter); err != nil {
		err = ref.ErrAddPrintf("PartMeta", err)
		return
	}
	for _, v := range baseResult.RuleDesc {
		partName = append(partName, v.Name)
	}
	baseResult.PartName = partName
	result.Result = baseResult
	return
}
func (pm PartitionMe) NameToRuleClip(parameter parDef.Parameter) (result global.Return, err error) {
	return
}
